package com.trippin.Utils;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Date;
import java.util.Set;
import java.util.TreeSet;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.google.appengine.repackaged.org.json.JSONException;
import com.google.appengine.repackaged.org.json.JSONObject;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.trippin.DB.entities.AttractionEntity;
import com.trippin.DB.entities.AttractionEntity.ShortDetailedAttraction;
import com.trippin.DB.entities.TripEntity;
import com.trippin.DB.entities.TripEntity.ShortDetailedTrip;
import com.trippin.DB.entities.TrippinEntity;
import com.trippin.servlets.Action;


public class ServletUtil {

	private static final Logger logger = Logger.getLogger(ServletUtil.class.getCanonicalName());

	public static String getResponseBody(Iterable<? extends TrippinEntity> entities) {
		StringBuilder sb = new StringBuilder();
		sb.append("{");
		sb.append(getObjectsAsJSON(entities));
		sb.append("}");
		return sb.toString();
	}
	
	public static String getResponseBody(TrippinEntity entity) {
		if(entity == null)
			return "{}";
		Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().serializeNulls().
				registerTypeAdapter(Date.class, new TrippinDateJsonSerializer()).create();
		return gson.toJson(entity);
	}


	/**
	 * 
	 * @param entities entities to return as JSON strings
	 * @return List the entities in JSON format
	 */
	private static String getObjectsAsJSON(Iterable<? extends TrippinEntity> entities) {
		StringBuilder sb = new StringBuilder();
		StringBuilder json = new StringBuilder();
		int i = 0;
		sb.append("[");
		for (TrippinEntity entity : entities) {
			sb.append(getResponseBody(entity));
			sb.append(", ");
			i++;
		}
		if (i > 0) {
			sb.deleteCharAt(sb.lastIndexOf(","));
		}  
		sb.append("]");
		
		json.append("\"size\" : " + i + ", \"data\" : ");
		json.append(sb);
		return json.toString();
	}
	
	public static void printError(HttpServletResponse resp, String action, String servletName, String info) throws IOException {
		logger.log(Level.SEVERE, ServletUtilConstants.ERROR_FAILED_TO_PERFORM_ACTION + action + " in " + servletName + " due to: " + info);
		resp.sendError(HttpServletResponse.SC_BAD_REQUEST,  ServletUtilConstants.ERROR_FAILED_TO_PERFORM_ACTION + action + ": " + info);
	}

	public static String getRequestBody(BufferedReader reader) throws IOException {
		StringBuffer sb = new StringBuffer();
		String line = null;
		while ((line = reader.readLine()) != null) {
				sb.append(line);
		}
		return sb.toString();
	}
	
	public static long getLongFromHeader(HttpServletRequest req, HttpServletResponse resp, String type, 
			String servletName, String actionName) throws IOException {
		String id = req.getHeader(type);
		try {
		long l = Long.parseLong(id);
		if((type.contains("Id")) && (l < 0)) {
			printError(resp,actionName,servletName,id + " is not a valid long");
		}
		return l;
		}catch(NumberFormatException nfe){
			printError(resp,actionName,servletName,id + " is not a valid long");
		}		
		return -1;
	}
	
	public static Action getActionFromHeader(HttpServletRequest req, HttpServletResponse resp, String servletName) throws IOException {
		boolean success = true;
		String actionName = req.getHeader(ServletUtilConstants.ACTION);
		if(actionName == null) {
			success = false;
		}
		Action action = Action.getActionByName(actionName);
		if(action == null){
			success = false;
		}
		
		if(!success) {
			printError(resp,actionName,servletName,ServletUtilConstants.ERROR_MISSING_ACTION_NAME);
		}
		return action;
	}

	public static void createResponse(HttpServletResponse resp, String actionName,
			String bodyResponse) throws IOException {
		resp.setContentType("application/json");
		resp.addHeader(ServletUtilConstants.ACTION, actionName + ServletUtilConstants.RESPONSE);
		PrintWriter out = resp.getWriter();
		out.print(bodyResponse);
		out.close();
	}

	public static Date getDateTime(HttpServletRequest req, String startOrEnd) {
		GsonBuilder gsonBuilder = new GsonBuilder().registerTypeAdapter(Date.class, new TrippinDateJsonDeserializer());
		Gson gson = gsonBuilder.create();
		String timeString = req.getHeader(startOrEnd);
		return gson.fromJson(timeString, Date.class);
	}

	public static void checkRequiredFields(String jsonString,
			String[] requireds, HttpServletResponse resp, String actionName, String servletName) throws IOException {
		JSONObject jo = null;
		String error = "";
		boolean success = true;
		int i = 0;
		try {
			jo = new JSONObject(jsonString);
		} catch (JSONException e) {
			success = false;
			error = jsonString + " is not a proper JSON";
		}
		try {
		for(; i < requireds.length; i++) {
					jo.get(requireds[i]);
				}
		} catch (JSONException e) {
			success = false;
			error = "missing required field: " + requireds[i];
			}
		finally {
			if(!success) {
				printError(resp,actionName,servletName,error);
			}
		}
	}


	public static TrippinEntity getFromJSON(String jsonString,
			Class<? extends TrippinEntity> TrippinEntityClass) {
		GsonBuilder gsonBuilder = new GsonBuilder().registerTypeAdapter(Date.class, new TrippinDateJsonDeserializer());
		Gson gson = gsonBuilder.create();
		return gson.fromJson(jsonString, TrippinEntityClass);		
	}
	
	
	public static Set<ShortDetailedAttraction> toShortAttractions(
			Iterable<AttractionEntity> attractions) {
		Set<ShortDetailedAttraction> shortaAttractions = new TreeSet<ShortDetailedAttraction>(new ShortDetailedAttractionListComparator());
		for(AttractionEntity att : attractions) {
			ShortDetailedAttraction sda = new ShortDetailedAttraction(att);
			shortaAttractions.add(sda);
		}
		return shortaAttractions;
	}
	
	public static Set<ShortDetailedTrip> toShortDetailedTrips (Iterable<TripEntity> trips) {
		Set<ShortDetailedTrip> shortTrips = new TreeSet<ShortDetailedTrip>(new ShortDetailedTripListComparator());
		for(TripEntity trip : trips) {
			ShortDetailedTrip sdt = new ShortDetailedTrip(trip);
			shortTrips.add(sdt);
		}
		return shortTrips;
	}


}
